﻿namespace FilterIllegalCharacter.Core
{
    /// <summary>
    /// 有限状态自动机DFA算法
    /// M=(Q, E, f, q0, Z)
    /// Q = 状态集合 即 此处所有的 node 字典  再加一个 空集
    /// E = 输入字符集合 即 所有敏感词所包含 单个字符 的集合
    /// f = 状态转换函数 即 单个敏感词字符作为Key 在 （所有node+空集）之间的索引关系 与空集索引返回其本身。（即E作为Key在Q中的索引关系）
    /// q0 = 唯一初态 即 空集，所有字符索引空集都返回其本身
    /// Z = 输出态集合 即 所有敏感词
    /// 复杂度为n*logn n为识别文本的长度
    /// </summary>
    public class WordNode
    {
        private Dictionary<int, WordNode> node;

        byte isEnd = 0;

        public WordNode()
        {
            node = new Dictionary<int, WordNode>();
        }

        /// <summary>
        /// 添加敏感词
        /// </summary>
        /// <param name="word"></param>
        public void Add(string word)
        {
            if (word.Length > 0)
            {
                int key = (int)word[0];
                if (!node.ContainsKey(key))
                {
                    node.Add(key, new WordNode());
                }
                WordNode subNode = node[key];
                if (word.Length > 1)
                {
                    subNode.Add(word.Substring(1));
                }
                else
                {
                    subNode.isEnd = 1;
                }
            }
        }

        /// <summary>
        /// 检测敏感词
        /// </summary>
        /// <param name="sourceDBCText">源字符串</param>
        /// <param name="cursor">游标</param>
        /// <param name="checkSpecialSym">特殊字符检测算法</param>
        /// <param name="specialSymOffset">特殊字符补偿</param>
        /// <returns></returns>
        public int checkAndGetEndIndex(string sourceDBCText, int cursor, Func<char, bool> checkSpecialSym, bool specialSymOffset = false)
        {
            //检测下位字符如果不是汉字 数字 字符 偏移量加1  
            for (int i = cursor; i < sourceDBCText.Length; i++)
            {
                if (specialSymOffset && checkSpecialSym != null && checkSpecialSym(sourceDBCText[i]))
                {
                    cursor++;
                }
                else break;
            }
            if (cursor >= sourceDBCText.Length) { return -1; }

            int key = sourceDBCText[cursor];
            if (node.ContainsKey(key) || node.ContainsKey('*'))
            {
                if (!node.ContainsKey(key))
                {
                    key = '*';
                }
                int index = -1;
                WordNode group = node[key];
                if (group.isEnd == 1)
                {
                    index = cursor;
                }
                int nextIndex = node[key].checkAndGetEndIndex(sourceDBCText, cursor + 1, checkSpecialSym, true);
                if (nextIndex > -1) 
                {
                    index = nextIndex;
                }
                return index;
            }
            return -1;
        }
    }
}